﻿/*
* Copyright (c) 2010-2012 Tesla Engine Group
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/


using System;
using Tesla.Graphics;
using Tesla.Math;
using XFG = Microsoft.Xna.Framework.Graphics;
using XNA = Microsoft.Xna.Framework;

namespace Tesla.Xna.Util {
    internal static class XNAHelper {

        internal static void ConvertVector2(ref Vector2 v, out XNA.Vector2 result) {
            result.X = v.X;
            result.Y = v.Y;
        }

        internal static void ConvertVector2(ref XNA.Vector2 v, out Vector2 result) {
            result.X = v.X;
            result.Y = v.Y;
        }

        internal static void ConvertVector3(ref Vector3 v, out XNA.Vector3 result) {
            result.X = v.X;
            result.Y = v.Y;
            result.Z = v.Z;
        }

        internal static void ConvertVector3(ref XNA.Vector3 v, out Vector3 result) {
            result.X = v.X;
            result.Y = v.Y;
            result.Z = v.Z;
        }

        internal static void ConvertVector4(ref Vector4 v, out XNA.Vector4 result) {
            result.X = v.X;
            result.Y = v.Y;
            result.Z = v.Z;
            result.W = v.W;
        }

        internal static void ConvertVector4(ref XNA.Vector4 v, out Vector4 result) {
            result.X = v.X;
            result.Y = v.Y;
            result.Z = v.Z;
            result.W = v.W;
        }

        internal static void ConvertColor(ref Color c, out XNA.Color result) {
            result = new XNA.Color();
            result.PackedValue = c.PackedValue;
        }

        internal static void ConvertColor(ref XNA.Color c, out Color result) {
            result.PackedValue = c.PackedValue;
        }

        internal static void ConvertQuaternion(ref Quaternion q, out XNA.Quaternion result) {
            result.X = q.X;
            result.Y = q.Y;
            result.Z = q.Z;
            result.W = q.W;
        }

        internal static void ConvertQuaternion(ref XNA.Quaternion q, out Quaternion result) {
            result.X = q.X;
            result.Y = q.Y;
            result.Z = q.Z;
            result.W = q.W;
        }

        internal static void ConvertMatrix(ref Matrix m, out XNA.Matrix result) {
            result.M11 = m.M11;
            result.M12 = m.M12;
            result.M13 = m.M13;
            result.M14 = m.M14;

            result.M21 = m.M21;
            result.M22 = m.M22;
            result.M23 = m.M23;
            result.M24 = m.M24;

            result.M31 = m.M31;
            result.M32 = m.M32;
            result.M33 = m.M33;
            result.M34 = m.M34;

            result.M41 = m.M41;
            result.M42 = m.M42;
            result.M43 = m.M43;
            result.M44 = m.M44;
        }

        internal static void ConvertMatrix(ref XNA.Matrix m, out Matrix result) {
            result.M11 = m.M11;
            result.M12 = m.M12;
            result.M13 = m.M13;
            result.M14 = m.M14;

            result.M21 = m.M21;
            result.M22 = m.M22;
            result.M23 = m.M23;
            result.M24 = m.M24;

            result.M31 = m.M31;
            result.M32 = m.M32;
            result.M33 = m.M33;
            result.M34 = m.M34;

            result.M41 = m.M41;
            result.M42 = m.M42;
            result.M43 = m.M43;
            result.M44 = m.M44;
        }

        internal static void ConvertMatrixTranspose(ref Matrix m, out XNA.Matrix result) {
            result.M11 = m.M11;
            result.M12 = m.M21;
            result.M13 = m.M31;
            result.M14 = m.M41;

            result.M21 = m.M12;
            result.M22 = m.M22;
            result.M23 = m.M32;
            result.M24 = m.M42;

            result.M31 = m.M13;
            result.M32 = m.M23;
            result.M33 = m.M33;
            result.M34 = m.M43;

            result.M41 = m.M14;
            result.M42 = m.M24;
            result.M43 = m.M34;
            result.M44 = m.M44;
        }

        internal static void ConvertMatrixTranspose(ref XNA.Matrix m, out Matrix result) {
            result.M11 = m.M11;
            result.M12 = m.M21;
            result.M13 = m.M31;
            result.M14 = m.M41;

            result.M21 = m.M12;
            result.M22 = m.M22;
            result.M23 = m.M32;
            result.M24 = m.M42;

            result.M31 = m.M13;
            result.M32 = m.M23;
            result.M33 = m.M33;
            result.M34 = m.M43;

            result.M41 = m.M14;
            result.M42 = m.M24;
            result.M43 = m.M34;
            result.M44 = m.M44;
        }

        internal static void ConvertViewport(ref Viewport vp, out XFG.Viewport result) {
            result = new XFG.Viewport(vp.X, vp.Y, vp.Width, vp.Height);
            result.MaxDepth = vp.MaxDepth;
            result.MinDepth = vp.MinDepth;
        }

        internal static void ConvertViewport(ref XFG.Viewport vp, out Viewport result) {
            result.X = vp.X;
            result.Y = vp.Y;
            result.Width = vp.Width;
            result.Height = vp.Height;
            result.MaxDepth = vp.MaxDepth;
            result.MinDepth = vp.MinDepth;
        }

        internal static void ConvertRectangle(ref Rectangle rect, out XNA.Rectangle result) {
            result.X = rect.X;
            result.Y = rect.Y;
            result.Width = rect.Width;
            result.Height = rect.Height;
        }

        internal static void ConvertRectangle(ref XNA.Rectangle rect, out Rectangle result) {
            result.X = rect.X;
            result.Y = rect.Y;
            result.Width = rect.Width;
            result.Height = rect.Height;
        }

        internal static XFG.VertexDeclaration ToXNAVertexDeclaration(VertexDeclaration decl) {
            VertexElement[] elems = decl.VertexElements;
            XFG.VertexElement[] xnaElements = new XFG.VertexElement[elems.Length];
            for(int i = 0; i < elems.Length; i++) {
                VertexElement elem = elems[i];
                XFG.VertexElement xnaElem  = new XFG.VertexElement();
                xnaElem.Offset = elem.Offset;
                xnaElem.UsageIndex = elem.SemanticIndex;
                switch(elem.SemanticName) {
                    case VertexSemantic.Position:
                        xnaElem.VertexElementUsage = XFG.VertexElementUsage.Position;
                        break;
                    case VertexSemantic.Normal:
                        xnaElem.VertexElementUsage = XFG.VertexElementUsage.Normal;
                        break;
                    case VertexSemantic.Binormal:
                        xnaElem.VertexElementUsage = XFG.VertexElementUsage.Binormal;
                        break;
                    case VertexSemantic.BlendIndices:
                        xnaElem.VertexElementUsage = XFG.VertexElementUsage.BlendIndices;
                        break;
                    case VertexSemantic.BlendWeight:
                        xnaElem.VertexElementUsage = XFG.VertexElementUsage.BlendWeight;
                        break;
                    case VertexSemantic.Color:
                        xnaElem.VertexElementUsage = XFG.VertexElementUsage.Color;
                        break;
                    case VertexSemantic.Depth:
                        xnaElem.VertexElementUsage = XFG.VertexElementUsage.Depth;
                        break;
                    case VertexSemantic.Fog:
                        xnaElem.VertexElementUsage = XFG.VertexElementUsage.Fog;
                        break;
                    case VertexSemantic.PointSize:
                        xnaElem.VertexElementUsage = XFG.VertexElementUsage.PointSize;
                        break;
                    case VertexSemantic.Tangent:
                        xnaElem.VertexElementUsage = XFG.VertexElementUsage.Tangent;
                        break;
                    case VertexSemantic.TextureCoordinate:
                        xnaElem.VertexElementUsage = XFG.VertexElementUsage.TextureCoordinate;
                        break;
                }

                switch(elem.Format) {
                    case VertexFormat.Vector4:
                        xnaElem.VertexElementFormat = XFG.VertexElementFormat.Vector4;
                        break;
                    case VertexFormat.Vector3:
                        xnaElem.VertexElementFormat = XFG.VertexElementFormat.Vector3;
                        break;
                    case VertexFormat.Vector2:
                        xnaElem.VertexElementFormat = XFG.VertexElementFormat.Vector2;
                        break;
                    case VertexFormat.Single:
                        xnaElem.VertexElementFormat = XFG.VertexElementFormat.Single;
                        break;
                    case VertexFormat.Color:
                        xnaElem.VertexElementFormat = XFG.VertexElementFormat.Color;
                        break;
                }
                xnaElements[i] = xnaElem;
            }
            return new XFG.VertexDeclaration(decl.VertexStride, xnaElements);
        }

        internal static XFG.PrimitiveType ToXNAPrimitiveType(PrimitiveType prim) {
            switch(prim) {
                case PrimitiveType.LineList:
                    return XFG.PrimitiveType.LineList;
                case PrimitiveType.LineStrip:
                    return XFG.PrimitiveType.LineStrip;
                case PrimitiveType.TriangleList:
                    return XFG.PrimitiveType.TriangleList;
                case PrimitiveType.TriangleStrip:
                    return XFG.PrimitiveType.TriangleStrip;
                default:
                    throw new InvalidCastException();
            }
        }

        internal static int GetPrimitiveCount(PrimitiveType primType, int vertexCount, bool useIndices, int indexCount) {
            switch(primType) {
                case PrimitiveType.TriangleStrip:
                    if(useIndices) {
                        return indexCount - 2;
                    } else {
                        return vertexCount - 2;
                    }
                case PrimitiveType.TriangleList:
                    if(useIndices) {
                        return indexCount / 3;
                    } else {
                        return vertexCount / 3;
                    }
                case PrimitiveType.LineStrip:
                    if(useIndices) {
                        return indexCount - 1;
                    } else {
                        return vertexCount - 1;
                    }
                case PrimitiveType.LineList:
                    if(useIndices) {
                        return indexCount / 2;
                    } else {
                        return vertexCount / 2;
                    }
                default:
                    return 0;
            }
        }

        internal static XFG.SurfaceFormat ToXNASurfaceFormat(SurfaceFormat format) {
            switch(format) {
                case SurfaceFormat.Vector4:
                    return XFG.SurfaceFormat.Vector4;
                case SurfaceFormat.Vector2:
                    return XFG.SurfaceFormat.Vector2;
                case SurfaceFormat.Single:
                    return XFG.SurfaceFormat.Single;
                case SurfaceFormat.RGBA64:
                    return XFG.SurfaceFormat.Rgba64;
                case SurfaceFormat.RGBA1010102:
                    return XFG.SurfaceFormat.Rgba1010102;
                case SurfaceFormat.RG32:
                    return XFG.SurfaceFormat.Rg32;
                case SurfaceFormat.DXT5:
                    return XFG.SurfaceFormat.Dxt5;
                case SurfaceFormat.DXT3:
                    return XFG.SurfaceFormat.Dxt3;
                case SurfaceFormat.DXT1:
                    return XFG.SurfaceFormat.Dxt1;
                case SurfaceFormat.Color:
                    return XFG.SurfaceFormat.Color;
                case SurfaceFormat.BGRA5551:
                    return XFG.SurfaceFormat.Bgra5551;
                case SurfaceFormat.BGR565:
                    return XFG.SurfaceFormat.Bgr565;
                case SurfaceFormat.Alpha8:
                    return XFG.SurfaceFormat.Alpha8;
                default:
                    throw new InvalidCastException();
            }
        }

        internal static SurfaceFormat FromXNASurfaceFormat(XFG.SurfaceFormat format) {
            switch(format) {
                case XFG.SurfaceFormat.Vector4:
                    return SurfaceFormat.Vector4;
                case XFG.SurfaceFormat.Vector2:
                    return SurfaceFormat.Vector2;
                case XFG.SurfaceFormat.Single:
                    return SurfaceFormat.Single;
                case XFG.SurfaceFormat.Rgba64:
                    return SurfaceFormat.RGBA64;
                case XFG.SurfaceFormat.Rgba1010102:
                    return SurfaceFormat.RGBA1010102;
                case XFG.SurfaceFormat.Rg32:
                    return SurfaceFormat.RG32;
                case XFG.SurfaceFormat.Dxt5:
                    return SurfaceFormat.DXT5;
                case XFG.SurfaceFormat.Dxt3:
                    return SurfaceFormat.DXT3;
                case XFG.SurfaceFormat.Dxt1:
                    return SurfaceFormat.DXT1;
                case XFG.SurfaceFormat.Color:
                    return SurfaceFormat.Color;
                case XFG.SurfaceFormat.Bgra5551:
                    return SurfaceFormat.BGRA5551;
                case XFG.SurfaceFormat.Bgr565:
                    return SurfaceFormat.BGR565;
                case XFG.SurfaceFormat.Alpha8:
                    return SurfaceFormat.Alpha8;
                default:
                    throw new InvalidCastException();
            }
        }

        internal static XFG.DepthFormat ToXNADepthFormat(DepthFormat format) {
            switch(format) {
                case DepthFormat.Depth32:
                case DepthFormat.Depth24:
                    return XFG.DepthFormat.Depth24;
                case DepthFormat.Depth24Stencil8:
                    return XFG.DepthFormat.Depth24Stencil8;
                case DepthFormat.Depth16:
                    return XFG.DepthFormat.Depth16;
            }
            return XFG.DepthFormat.None;
        }

        internal static XFG.CubeMapFace ToXNACubeMapFace(CubeMapFace face) {
            switch(face) {
                case CubeMapFace.NegativeX:
                    return XFG.CubeMapFace.NegativeX;
                case CubeMapFace.NegativeY:
                    return XFG.CubeMapFace.NegativeY;
                case CubeMapFace.NegativeZ:
                    return XFG.CubeMapFace.NegativeZ;
                case CubeMapFace.PositiveX:
                    return XFG.CubeMapFace.PositiveX;
                case CubeMapFace.PositiveY:
                    return XFG.CubeMapFace.PositiveY;
                case CubeMapFace.PositiveZ:
                    return XFG.CubeMapFace.PositiveZ;
                default:
                    throw new InvalidCastException();
            }
        }

        internal static XFG.TextureAddressMode ToXNATextureAddressMode(TextureAddressMode mode) {
            switch(mode) {
                case TextureAddressMode.Clamp:
                    return XFG.TextureAddressMode.Clamp;
                case TextureAddressMode.Mirror:
                    return XFG.TextureAddressMode.Mirror;
                case TextureAddressMode.Wrap:
                    return XFG.TextureAddressMode.Wrap;
                default:
                    throw new InvalidCastException();
            }
        }

        internal static XFG.TextureFilter ToXNATextureFilter(TextureFilter filter) {
            switch(filter) {
                case TextureFilter.Anisotropic:
                    return XFG.TextureFilter.Anisotropic;
                case TextureFilter.Linear:
                    return XFG.TextureFilter.Linear;
                case TextureFilter.LinearMipPoint:
                    return XFG.TextureFilter.LinearMipPoint;
                case TextureFilter.MinLinearMagPointMipLinear:
                    return XFG.TextureFilter.MinLinearMagPointMipLinear;
                case TextureFilter.MinLinearMagPointMipPoint:
                    return XFG.TextureFilter.MinLinearMagPointMipPoint;
                case TextureFilter.MinPointMagLinearMipLinear:
                    return XFG.TextureFilter.MinPointMagLinearMipLinear;
                case TextureFilter.MinPointMagLinearMipPoint:
                    return XFG.TextureFilter.MinPointMagLinearMipPoint;
                case TextureFilter.Point:
                    return XFG.TextureFilter.Point;
                case TextureFilter.PointMipLinear:
                    return XFG.TextureFilter.PointMipLinear;
                default:
                    throw new InvalidCastException();
            }
        }

        internal static XFG.CompareFunction ToXNACompareFunction(ComparisonFunction func) {
            switch(func) {
                case ComparisonFunction.Always:
                    return XFG.CompareFunction.Always;
                case ComparisonFunction.Equal:
                    return XFG.CompareFunction.Equal;
                case ComparisonFunction.Greater:
                    return XFG.CompareFunction.Greater;
                case ComparisonFunction.GreaterEqual:
                    return XFG.CompareFunction.GreaterEqual;
                case ComparisonFunction.Less:
                    return XFG.CompareFunction.Less;
                case ComparisonFunction.LessEqual:
                    return XFG.CompareFunction.LessEqual;
                case ComparisonFunction.Never:
                    return XFG.CompareFunction.Never;
                case ComparisonFunction.NotEqual:
                    return XFG.CompareFunction.NotEqual;
                default:
                    throw new InvalidCastException();
            }
        }

        internal static XFG.CullMode ToXNACullMode(CullMode mode, VertexWinding winding) {
            if(mode == CullMode.None) {
                return XFG.CullMode.None;
            }

            switch(winding) {
                case VertexWinding.Clockwise:
                    switch(mode) {
                        case CullMode.Back:
                            return XFG.CullMode.CullCounterClockwiseFace;
                        case CullMode.Front:
                            return XFG.CullMode.CullClockwiseFace;
                        default:
                            throw new InvalidCastException();
                    }
                case VertexWinding.CounterClockwise:
                    switch(mode) {
                        case CullMode.Back:
                            return XFG.CullMode.CullClockwiseFace;
                        case CullMode.Front:
                            return XFG.CullMode.CullCounterClockwiseFace;
                        default:
                            throw new InvalidCastException();
                    }
                default:
                    throw new InvalidCastException();
            }
        }

        internal static XFG.FillMode ToXNAFillMode(FillMode mode) {
            switch(mode) {
                case FillMode.Solid:
                    return XFG.FillMode.Solid;
                case FillMode.WireFrame:
                    return XFG.FillMode.WireFrame;
                default:
                    throw new InvalidCastException();
            }
        }

        internal static XFG.StencilOperation ToXNAStencilOperation(StencilOperation op) {
            switch(op) {
                case StencilOperation.Decrement:
                    return XFG.StencilOperation.Decrement;
                case StencilOperation.DecrementAndClamp:
                    return XFG.StencilOperation.DecrementSaturation;
                case StencilOperation.Increment:
                    return XFG.StencilOperation.Increment;
                case StencilOperation.IncrementAndClamp:
                    return XFG.StencilOperation.IncrementSaturation;
                case StencilOperation.Invert:
                    return XFG.StencilOperation.Invert;
                case StencilOperation.Keep:
                    return XFG.StencilOperation.Keep;
                case StencilOperation.Replace:
                    return XFG.StencilOperation.Replace;
                case StencilOperation.Zero:
                    return XFG.StencilOperation.Zero;
                default:
                    throw new InvalidCastException();
            }
        }

        internal static XFG.Blend ToXNABlend(Blend blend) {
            switch(blend) {
                case Blend.BlendFactor:
                    return XFG.Blend.BlendFactor;
                case Blend.DestinationAlpha:
                    return XFG.Blend.DestinationAlpha;
                case Blend.DestinationColor:
                    return XFG.Blend.DestinationColor;
                case Blend.InverseBlendFactor:
                    return XFG.Blend.InverseBlendFactor;
                case Blend.InverseDestinationAlpha:
                    return XFG.Blend.InverseDestinationAlpha;
                case Blend.InverseDestinationColor:
                    return XFG.Blend.InverseDestinationColor;
                case Blend.InverseSourceAlpha:
                    return XFG.Blend.InverseSourceAlpha;
                case Blend.InverseSourceColor:
                    return XFG.Blend.InverseSourceColor;
                case Blend.One:
                    return XFG.Blend.One;
                case Blend.SourceAlpha:
                    return XFG.Blend.SourceAlpha;
                case Blend.SourceAlphaSaturation:
                    return XFG.Blend.SourceAlphaSaturation;
                case Blend.SourceColor:
                    return XFG.Blend.SourceColor;
                case Blend.Zero:
                    return XFG.Blend.Zero;
                default:
                    throw new InvalidCastException();
            }
        }

        internal static XFG.BlendFunction ToXNABlendFunction(BlendFunction func) {
            switch(func) {
                case BlendFunction.Add:
                    return XFG.BlendFunction.Add;
                case BlendFunction.Max:
                    return XFG.BlendFunction.Max;
                case BlendFunction.Min:
                    return XFG.BlendFunction.Min;
                case BlendFunction.ReverseSubtract:
                    return XFG.BlendFunction.ReverseSubtract;
                case BlendFunction.Subtract:
                    return XFG.BlendFunction.Subtract;
                default:
                    throw new InvalidCastException();
            }
        }

        internal static XFG.RenderTargetUsage ToXNARenderTargetUsage(RenderTargetUsage renderTargetUsage) {
            switch(renderTargetUsage) {
                case RenderTargetUsage.DiscardContents:
                    return XFG.RenderTargetUsage.DiscardContents;
                case RenderTargetUsage.PlatformDefault:
                    return XFG.RenderTargetUsage.PlatformContents;
                case RenderTargetUsage.PreserveContents:
                    return XFG.RenderTargetUsage.PreserveContents;
                default:
                    throw new InvalidCastException();
            }
        }

        internal static XFG.PresentInterval ToXNAPresentInterval(PresentInterval presentInterval) {
            switch(presentInterval) {
                case PresentInterval.Immediate:
                    return XFG.PresentInterval.Immediate;
                case PresentInterval.One:
                    return XFG.PresentInterval.One;
                case PresentInterval.Two:
                    return XFG.PresentInterval.Two;
                default:
                    throw new InvalidCastException();
            }
        }
    }
}
